Fork me on GitHub

Mybatis 解析xml源码

mybatis-config xml配置及java解析

xml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

<properties resource="config/generator.properties"/>

<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>



<typeAliases>
<package name="com.yuanwj.mybatisdemo.model"/>
</typeAliases>


<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="driver" value="${jdbc.driver}"/>
</dataSource>
</environment>
</environments>

<mappers>
<mapper resource="mybatis/mapper/PhoneImeiMapper.xml"/>
</mappers>


</configuration>

java解析核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 通过配置解析各个节点,每个节点生成XNode,并通过XNode获取其对应的配置属性
private void parseConfiguration(XNode root) {
try {
Properties settings = this.settingsAsPropertiess(root.evalNode("settings"));
this.propertiesElement(root.evalNode("properties"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectionFactoryElement(root.evalNode("reflectionFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}

解析配置文件

mybatis解析核心位于parsing包,如下图
parsing

  1. XNode类:每个标签就是一节点

  2. XPathParser: XPath 类的一个包装,主要的逻辑就是该类中实现的。

  3. PropertyParser : 属性解析器

  4. TokenHandler : 占位符解析器,是一个接口,由子类自己实现解析规则

  5. GenericTokenParser : 通用的占位符解析器,用来处理 #{} 和 ${} 参数

通过上述代码可知XNode为解析结果,下面代码分析解析过程及上述各个类作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    //上述代码调用方法  位于XNode
public XNode evalNode(String expression) {//通过节点名解析
return this.xpathParser.evalNode(this.node, expression);
}

// XPathParser.class
public XNode evalNode(Object root, String expression) {
Node node = (Node)this.evaluate(expression, root, XPathConstants.NODE);
return node == null ? null : new XNode(this, node, this.variables);//生成XNode
}

// XPathParser.class 新建XNode
public XNode(XPathParser xpathParser, Node node, Properties variables) {
this.xpathParser = xpathParser;
this.node = node;
this.name = node.getNodeName();
this.variables = variables;
this.attributes = this.parseAttributes(node); //解析属性,关键方法
this.body = this.parseBody(node); //子节点
}

// XPathParser.class 解析属性
private Properties parseAttributes(Node n) {
Properties attributes = new Properties();
NamedNodeMap attributeNodes = n.getAttributes();
if (attributeNodes != null) {
for(int i = 0; i < attributeNodes.getLength(); ++i) { //遍历属性
Node attribute = attributeNodes.item(i);
String value = PropertyParser.parse(attribute.getNodeValue(), this.variables); //通过PropertyParser类解析属性值
attributes.put(attribute.getNodeName(), value); //获取属性值,放入properties中
}
}
return attributes;
}
// PropertyParser.class
public class PropertyParser {
private PropertyParser() {
}

public static String parse(String string, Properties variables) {
PropertyParser.VariableTokenHandler handler = new PropertyParser.VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);// 通过GenericTokenParser解析
return parser.parse(string);
}

private static class VariableTokenHandler implements TokenHandler {
private Properties variables;

public VariableTokenHandler(Properties variables) {
this.variables = variables;
}

//获取${} 配置属性
public String handleToken(String content) {
return this.variables != null && this.variables.containsKey(content) ? this.variables.getProperty(content) : "${" + content + "}";
}
}
}
// GenericTokenParser.class 核心,读取配置属性值,如果是${},则读取配置,否则直接访问value值
public String parse(String text) {
StringBuilder builder = new StringBuilder();
StringBuilder expression = new StringBuilder();
if (text != null && text.length() > 0) {
char[] src = text.toCharArray();
int offset = 0;

for(int start = text.indexOf(this.openToken, offset); start > -1; start = text.indexOf(this.openToken, offset)) {
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(this.openToken);
offset = start + this.openToken.length();
} else {
expression.setLength(0);
builder.append(src, offset, start - offset);
offset = start + this.openToken.length();

int end;
for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
if (end <= offset || src[end - 1] != '\\') {
expression.append(src, offset, end - offset);
int var10000 = end + this.closeToken.length();
break;
}

expression.append(src, offset, end - offset - 1).append(this.closeToken);
offset = end + this.closeToken.length();
}

if (end == -1) {
builder.append(src, start, src.length - start);
offset = src.length;
} else {
builder.append(this.handler.handleToken(expression.toString()));
offset = end + this.closeToken.length();
}
}
}

if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
}

return builder.toString();
}
显示 Gitment 评论